/**
 * 
 */
package info.noahcampbell.meter.management.plugin;

import info.noahcampbell.meter.management.GraphServlet;
import info.noahcampbell.meter.management.reader.RraConfiguredGraph;
import info.noahcampbell.meter.messenger.accumulator.Accumulator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jrobin.core.RrdException;
import org.jrobin.graph.RrdGraphDef;

/**
 * This is session level object that is intended for modification by the user
 * and to interact with the underlying RrdManager.
 * 
 * @author Noah Campbell
 * @version 1.0
 */
public class GraphBuilderBean {
    
    /**
     * Construct a new <code>GraphBuilderBean</code>.
     */
    public GraphBuilderBean() { }
    
    /** The def. */
    private RrdGraphDef def = new RrdGraphDef();
    
    /** The dirty. */
    private boolean dirty;
    
    
    /** The dss. */
    private HashMap<String,Source> dss = new HashMap<String,Source>();
    
    /**
     * Return all the ObjectNames
     * 
     * @return objectnames
     * @throws Exception 
     */
    public List<String> getChartableMBeans() throws Exception {
        ArrayList<String> results = new ArrayList<String>();
        for(Accumulator a : RrdManager.getGraphableObjectNames()) {
            results.add(a.getSourceMBean().getCanonicalName());
        }
        return results;
    }
    
    
    /**
     * @return datasources
     */
    public List<Source> getDatasources() {
        return new ArrayList<Source>(dss.values());
    }
    
    
    /**
     * @return listNames
     */
    public List<String> getDataSourceNames() {
        ArrayList<String> results = new ArrayList<String>();
        for(Source m : dss.values()) {
            results.add(m.getItem());
        }
        return results;
    }
    
    
    /**
     * @param objectName
     * @return datasource
     */
    private String lookupDatasource(String objectName) {
        for(Accumulator a : RrdManager.accumulators.values()) {
            if(a.getSourceMBean().getCanonicalName().equalsIgnoreCase(objectName)) {
                return a.getPath();
            }
        }
        throw new IllegalArgumentException(objectName + " not found");
    }
    
    
    /**
     * @param item
     * @param ds
     * @param source
     * @param confunc
     * @throws Exception
     */
    public void addDatasource(String item, String ds, String source, String confunc) throws Exception {
        String lookupDatasource = lookupDatasource(ds);
        def.datasource(item, lookupDatasource, source, confunc);
        dss.put(item, new Source(ds, confunc, item, source));
        dirty = true;
    }
    
    
    /**
     * @param name
     */
    public void removeDatasource(String name) {
        dss.remove(name);
    }
       
    /**
     * @param type
     * @param item
     * @param color
     * @param title
     * @throws Exception
     */
    public void addGraph(String type, String item, String color, String title) throws Exception {
        
        if(type.equalsIgnoreCase("LINE")) {
            def.line(item, GraphServlet.parseColor(color), title);
        } else if (type.equalsIgnoreCase("AREA")) {
            def.area(item, GraphServlet.parseColor(color), title);
        } else if (type.equalsIgnoreCase("STACK")) {
            def.stack(item, GraphServlet.parseColor(color), title);
        } else if (type.equals("HRULE")){
            def.hrule(1.0d, GraphServlet.parseColor(color), title);
        } else {
            throw new IllegalArgumentException("Unknown");
        }
        
        lookup(item).addGraphElement(type, color, title);
        dirty = true;
    }
    
    
    
    /**
     * @param name
     * @param concfun
     * @param gprint
     * @throws Exception 
     */
    public void addLegend(String name, String concfun, String gprint) throws Exception {
        def.gprint(name, concfun, gprint);
        lookup(name).addLegend(concfun, gprint);
        dirty = true;
    }
    
    
    
    /**
     * @param name
     * @return source
     * @throws Exception 
     */
    private Source lookup(String name) throws Exception {
       Source s = dss.get(name);
       if(s == null) throw new Exception("Unable to locate " + name);
       return s;
    }
    
    /**
     * @return elements A list of Maps
     */
    public List<Map<String,String>> getGraphElements() {
        return null;
    }
    
    
    /**
     * @return rawDatasources
     */
    public List<String> getRawDatasources() {
        return Collections.unmodifiableList(RrdManager.getRraPaths());
    }
    
    
    /**
     * @param objectName
     * @return list
     * @throws Exception
     */
    public List<String> getResources(String objectName) throws Exception {
        ArrayList<String> results = new ArrayList<String>();
        for(Accumulator a : RrdManager.accumulators.values()) {
            if(a.getSourceMBean().getCanonicalName().equals(objectName)) {
                return a.getDatasourceNames();
            }
        }
        
        return results;
    }
    
    
    /**
     * If the RrdGraphDef has been changed since it was last saved.
     * 
     * @return dirty
     */
    public boolean dirty() {
        return dirty;
    }
    
    
    /**
     * Store the RrdGraphDef
     * @throws Exception 
     */
    public void store(String name) throws Exception {
        if(name == null || name.length() <= 0) {
            name = new Date().toString();
        }
        RrdManager.instance().storeTemplate(name, this.def.exportXmlTemplate());
    }
    
    
    /**
     * Throw away any work in the session.
     */
    public void discard() {
        this.dss.clear();
        this.def = new RrdGraphDef();
        this.dirty = false;
    }

    
    /**
     * @return list A lsit of RraConfiguredGraph objects.
     * @throws Exception
     */
    public List<RraConfiguredGraph> getTemplates() throws Exception {
        return RrdManager.instance().listTemplates();
    }
    
    /**
     * Get the RrdGraphDef for this session.
     * @return def
     */
    public RrdGraphDef getRrdGraphDef() {
        return this.def;
    }
    
    public static class Source {
        private String item;
        private String ds;
        private String function;
        private String rraSource;
        
        private List<Legend> legends = new ArrayList<Legend>();
        private List<GraphElement> ge = new ArrayList<GraphElement>();
        
        public Source(String ds, String function, String item, String source) {
            this.ds = ds;
            this.function = function;
            this.item = item;
            this.rraSource = source;
        }
        
        public List<Legend> getLegends() {
            return this.legends;
        }
        
        public List<GraphElement> getGraphs() {
            return this.ge;
        }

        /**
         * @return Returns the ds.
         */
        public String getDs() {
            return this.ds;
        }

        /**
         * @param ds The ds to set.
         */
        public void setDs(String ds) {
            this.ds = ds;
        }

        /**
         * @return Returns the function.
         */
        public String getFunction() {
            return this.function;
        }

        /**
         * @param function The function to set.
         */
        public void setFunction(String function) {
            this.function = function;
        }

        /**
         * @return Returns the item.
         */
        public String getItem() {
            return this.item;
        }

        /**
         * @param item The item to set.
         */
        public void setItem(String item) {
            this.item = item;
        }

        /**
         * @return Returns the rraSource.
         */
        public String getRraSource() {
            return this.rraSource;
        }

        /**
         * @param rraSource The rraSource to set.
         */
        public void setRraSource(String rraSource) {
            this.rraSource = rraSource;
        }
        
        public GraphElement addGraphElement(String type, String color, String title) {
            
            GraphElement graphElement = new GraphElement(type, color, title) {

                @Override
                public String getName() {
                    return item;
                }
                
            };
            ge.add(graphElement);
            return graphElement;
        }
        
        
        /**
         * @param function
         * @param gprint
         * @return
         */
        public Legend addLegend(String function, String gprint) {
            Legend legend = new Legend(function,gprint){

                @Override
                public String getName() {
                    return item;
                }
                
            };
            legends.add(legend);
            return legend;
        }
        
        
    }
    
    public abstract static class GraphElement {
        
        private String type;
        private String color;
        private String title;
        public GraphElement(String type, String color, String title) {
            this.type = type;
            this.color = color;
            this.title = title;
        }
        /**
         * @return Returns the color.
         */
        public String getColor() {
            return this.color;
        }
        /**
         * @param color The color to set.
         */
        public void setColor(String color) {
            this.color = color;
        }
        /**
         * @return Returns the title.
         */
        public String getTitle() {
            return this.title;
        }
        /**
         * @param title The title to set.
         */
        public void setTitle(String title) {
            this.title = title;
        }
        
        public abstract String getName();
        /**
         * @return Returns the type.
         */
        public String getType() {
            return this.type;
        }
        /**
         * @param type The type to set.
         */
        public void setType(String type) {
            this.type = type;
        }
    }
    
    public abstract static class Legend {
        private String function;
        private String gprint;
        /**
         * Construct a new <code>Legend</code>.
         *
         * @param function
         * @param gprint
         * @param source
         */
        public Legend(String function, String gprint) {
            this.function = function;
            this.gprint = gprint;
        }
        /**
         * @return Returns the function.
         */
        public String getFunction() {
            return this.function;
        }
        /**
         * @param function The function to set.
         */
        public void setFunction(String function) {
            this.function = function;
        }
        /**
         * @return Returns the gprint.
         */
        public String getGprint() {
            return this.gprint;
        }
        /**
         * @param gprint The gprint to set.
         */
        public void setGprint(String gprint) {
            this.gprint = gprint;
        }
        
        public abstract String getName();
    }
}
